/*******************************************************************************
GControl.cpp

	Author:		LiuDingXi
	e-mail:		liudx@gmail.com
	Date time:	2009-08-28

	Copyright (C) 2009
	All rights reserved.
	
*******************************************************************************/
#include "StdAfx.h"
#include "GControl.h"
#include "GUtil.h"

#if (_OS_ == _OS_WINNT_)
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
#endif//(_OS_ == _OS_WINNT_)

////////////////////////////////////////////////////////////////////////////////

GControl::GControl()
{
	m_pBase = NULL;
	m_pThis = NULL;

	m_pCtrls = NULL;

	m_pParent = NULL;

	m_bEnable = true;
}

GControl::~GControl()
{
	if(m_pCtrls)
	{
		const CONTROL* pThis = (const CONTROL*)m_pThis;

		for(int i=0; i<pThis->nItem; i++)
		{
			const CITEM& refItem = pThis->pItem[i];
			delete m_pCtrls[i].pObject;
		}
		delete[] m_pCtrls;
		m_pCtrls = NULL;
	}
}

BOOL GControl::Create(GSight* pSight, GControl* pParent, UINT nID)
{
	if(pParent)
	{
		m_pParent = pParent;
		return m_pParent->InitItem(pSight, this, nID);
	}
	return FALSE;
}

BOOL GControl::InitItem(GSight* pSight, GObject* pItem, UINT nID)
{
	const CONTROL* pThis = (const CONTROL*)m_pThis;

	if(pThis && pThis->nItem > 0)
	{
		if((SINT)nID >= 0 && (SINT)nID < pThis->nItem)
		{
			const CITEM& refItem = pThis->pItem[nID];
			return pItem->DataBind(pSight, m_pBase, refItem.pItem);
		}
	}
	return FALSE;
}

BOOL GControl::DataBind(GSight* pSight, const BYTE* pBase, SceneDat::PTR ptr)
{
	m_pBase = pBase;
	m_pThis = (const CONTROL*)(pBase + ptr);

	const CONTROL* pThis = (const CONTROL*)m_pThis;

	if(pThis->nItem > 0)
	{
		m_pCtrls = new GCTRL[pThis->nItem];
		if(m_pCtrls)
		{
			for(int i=0; i<pThis->nItem; i++)
			{
				const CITEM& refItem = pThis->pItem[i];
				switch(refItem.nType)
				{
				case SceneDat::tSprite:
					m_pCtrls[i].pSprite = new GSprite();
					m_pCtrls[i].pSprite->DataBind(pSight, pBase, refItem.pItem);
					m_pCtrls[i].pSprite->SetVisible(true);
					break;
				default:
					m_pCtrls[i].pControl = NewCtrl(refItem.nType);
					m_pCtrls[i].pControl->m_pParent = this;
					m_pCtrls[i].pControl->DataBind(pSight, pBase, refItem.pItem);
					break;
				}
			}
		}
	}
	return TRUE;
}

GControl* GControl::NewCtrl(SceneDat::INT nType)const
{
	GControl* pCtrl = NULL;
	switch(nType)
	{
	case SceneDat::tButton:
		pCtrl = new GButton();
		break;
	case SceneDat::tCheckBox:
		pCtrl = new GCheckBox();
		break;
	case SceneDat::tProgress:
		pCtrl = new GProgress();
		break;
	case SceneDat::tTabHead:
		pCtrl = new GTabHead();
		break;
	case SceneDat::tTabView:
		pCtrl = new GTabView();
		break;
	case SceneDat::tEditor:
		pCtrl = new GEditor();
		break;
	default:
		pCtrl = new GControl();
		break;
	}
	return pCtrl;
}

void GControl::OnDraw(GDC* pDC)
{
	if(m_pCtrls)
	{
		const CONTROL* pThis = (const CONTROL*)m_pThis;

		for(int i=0; i<pThis->nItem; i++)
		{
			if(m_pCtrls[i].pObject->IsVisible())
			{
				const CITEM& refItem = pThis->pItem[i];

				int x = m_nPosX + refItem.nPosX;
				int y = m_nPosY + refItem.nPosY;

				m_pCtrls[i].pObject->SetPos(x, y);
				m_pCtrls[i].pObject->OnDraw(pDC);
			}
		}
	}
}


void GControl::Update(UINT nMillisecond)
{
	if(m_pCtrls)
	{
		const CONTROL* pThis = (const CONTROL*)m_pThis;

		for(int i=0; i<pThis->nItem; i++)
		{
			m_pCtrls[i].pObject->Update(nMillisecond);
		}
	}
}

bool GControl::IsVisible()const
{
	if(m_pCtrls)
	{
		return m_bVisible;
	}
	return false;
}

BOOL GControl::GetPos(POS& refPos)const
{
	CXY refCxy;
	if(GetCxy(refCxy))
	{
		refPos.x = m_nPosX;
		refPos.y = m_nPosY;
		refPos.cx = refCxy.cx;
		refPos.cy = refCxy.cy;

		return TRUE;
	}
	return FALSE;
}

BOOL GControl::GetCxy(CXY& refCxy)const
{
	const CONTROL* pThis = (const CONTROL*)m_pThis;

	if(m_pCtrls && pThis->nItem > 0)
	{
		const CITEM& refItem = pThis->pItem[0];

		CXY sz;

		m_pCtrls[0].pObject->GetCxy(sz);

		refCxy.cx = refItem.nPosX + sz.cx;
		refCxy.cy = refItem.nPosY + sz.cy;

		for(int i=1; i<pThis->nItem; i++)
		{
			const CITEM& refItem = pThis->pItem[i];
			
			m_pCtrls[0].pObject->GetCxy(sz);

			int cx = refItem.nPosX + sz.cx;
			int cy = refItem.nPosY + sz.cy;

			if(cx > refCxy.cx)
				refCxy.cx = cx;
			if(cy > refCxy.cy)
				refCxy.cy = cy;
		}
		return TRUE;
	}
	return FALSE;
}


BOOL GControl::GetBox(BOX& refBox)const
{
	CXY refCxy;
	if(GetCxy(refCxy))
	{
		refBox.left = m_nPosX;
		refBox.top = m_nPosY;
		refBox.right = m_nPosX + refCxy.cx;
		refBox.bottom = m_nPosY + refCxy.cy;

		return TRUE;
	}
	return FALSE;
}

SINT GControl::HitTest(int x, int y)const
{
	BOX refBox;
	if(GetBox(refBox))
	{
		if( x > refBox.left &&
			y > refBox.top &&
			x < refBox.right &&
			y < refBox.bottom )
		{
			return 0;
		}

	}
	return -1;
}

PMRV GControl::ProcMsg(GMsg& refMsg)
{
	if(m_pCtrls)
	{
		const CONTROL* pThis = (const CONTROL*)m_pThis;

		for(int i=0; i<pThis->nItem; i++)
		{
			if(m_pCtrls[i].pObject->IsVisible())
			{
				const CITEM& refItem = pThis->pItem[i];

				PMRV nRet = m_pCtrls[i].pObject->ProcMsg(refMsg);
				if(nRet != PM_PASS)
				{
					return nRet;
				}
			}
		}

	}

	return PM_PASS;
}


//////////////////////////////////////////////////////////////////////////

GDlgBox::GDlgBox()
{
}

GDlgBox::~GDlgBox()
{
}

PMRV GDlgBox::ProcMsg(GMsg& refMsg)
{
	const CONTROL* pThis = (const CONTROL*)m_pThis;

	if(pThis && pThis->nItem > 0)
	{
		switch(refMsg.nMsg)
		{
		case MSG_LBUTTONDOWN:
		case MSG_LBUTTONUP:
			if(HitTest(refMsg.x, refMsg.y) != -1)
			{
				refMsg.nNotify = NTF_INSIDE;
			}
			else
			{
				refMsg.nNotify = NTF_OUTSIDE;
			}
			return PM_NEXT;
		}
	}
	return PM_PASS;
}


GButton::GButton()
{
}

GButton::~GButton()
{
}

PMRV GButton::ProcMsg(GMsg& refMsg)
{
	const CONTROL* pThis = (const CONTROL*)m_pThis;

	if(pThis && pThis->nItem > 0)
	{
		switch(refMsg.nMsg)
		{
		case MSG_LBUTTONDOWN:
			if(HitTest(refMsg.x, refMsg.y) != -1)
			{
				refMsg.nCtrlId = m_nCtrlId;
				refMsg.nNotify = NTF_CLICK_DN;
				OnPressed(true);
				return PM_NEXT;
			}
			else
			{
				OnPressed(false);
			}
			break;

		case MSG_LBUTTONUP:
			OnPressed(false);
			if(HitTest(refMsg.x, refMsg.y) != -1)
			{
				refMsg.nCtrlId = m_nCtrlId;
				refMsg.nNotify = NTF_CLICK_UP;
				return PM_NEXT;
			}
			break;
		case MSG_MOUSEMOVE:
			break;
		}
	}
	return PM_PASS;
}

void GButton::OnPressed(bool bTrue)
{
	const CONTROL* pThis = (const CONTROL*)m_pThis;

	if(pThis && m_pCtrls)
	{
		for(int i=0; i<pThis->nItem; i++)
		{
			const CITEM& refItem = pThis->pItem[i];
			if(refItem.nType == SceneDat::tSprite)
			{
				GSprite* pSprite = m_pCtrls[i].pSprite;
				if(bTrue)
				{
					pSprite->SetActFrame(1);
				}
				else
				{
					pSprite->SetActFrame(0);
				}
			}
		}
	}
}

GCheckBox::GCheckBox()
{
	m_bChecked = false;
}

GCheckBox::~GCheckBox()
{
}

PMRV GCheckBox::ProcMsg(GMsg& refMsg)
{
	const CONTROL* pThis = (const CONTROL*)m_pThis;

	if(pThis && m_bEnable)
	{
		switch(refMsg.nMsg)
		{
		case MSG_LBUTTONDOWN:
			if(HitTest(refMsg.x, refMsg.y) != -1)
			{
				refMsg.nNotify = NTF_SELCHANGED;
				SetCheck(!m_bChecked);
				return PM_NEXT;
			}
			break;
		}
	}
	return PM_PASS;
}

bool GCheckBox::SetCheck(bool bCheck)
{
	bool bOld = m_bChecked;
	if(bCheck != bOld)
	{
		const CONTROL* pThis = (const CONTROL*)m_pThis;

		m_bChecked = bCheck;
		if(pThis && m_pCtrls)
		{
			int nIndex = 0;
			if(m_bEnable)
			{
				nIndex = m_bChecked ? 2 : 0;
			}
			else
			{
				nIndex = m_bChecked ? 3 : 1;
			}

			for(int i=0; i<pThis->nItem; i++)
			{
				const CITEM& refItem = pThis->pItem[i];
				if(refItem.nType == SceneDat::tSprite)
				{
					GSprite* pSprite = m_pCtrls[i].pSprite;
					pSprite->SetActFrame(nIndex);
				}
			}
		}

	}
	return bOld;
}

bool GCheckBox::SetEnable(bool bEnable)
{
	bool bOld = m_bEnable;
	if(bEnable != bOld)
	{
		const CONTROL* pThis = (const CONTROL*)m_pThis;

		m_bEnable = bEnable;
		if(pThis && m_pCtrls)
		{
			int nIndex = 0;
			if(m_bEnable)
			{
				nIndex = m_bChecked ? 2 : 0;
			}
			else
			{
				nIndex = m_bChecked ? 3 : 1;
			}

			for(int i=0; i<pThis->nItem; i++)
			{
				const CITEM& refItem = pThis->pItem[i];
				if(refItem.nType == SceneDat::tSprite)
				{
					GSprite* pSprite = m_pCtrls[i].pSprite;
					pSprite->SetActFrame(nIndex);
				}
			}
		}
	}
	return bOld;
}

GProgress::GProgress()
{
	m_nPos = 0;
}

GProgress::~GProgress()
{
}

SINT GProgress::GetPos()const
{
	return m_nPos;
}

SINT GProgress::SetPos(SINT nPos)
{
	SINT nOld = m_nPos;
	if(nPos >= 0 && nPos <= 100)
	{
		m_nPos = nPos;
	}
	return nOld;
}

void GProgress::OnDraw(GDC* pDC)
{
	if(m_pCtrls)
	{
		const CONTROL* pThis = (const CONTROL*)m_pThis;

		if(m_nPos > 0)
		{
			CXY refCxy;
			if(m_pCtrls[0].pObject->GetCxy(refCxy))
			{
				const CITEM& refItem = pThis->pItem[0];
				
				int x = m_nPosX + refItem.nPosX;
				int y = m_nPosY + refItem.nPosY;

				for(int i=0; i<m_nPos; i++)
				{
					m_pCtrls[0].pObject->SetPos(x, y);
					m_pCtrls[0].pObject->OnDraw(pDC);
					x += refCxy.cx;
				}
			}
		}

		for(int i=1; i<pThis->nItem; i++)
		{
			const CITEM& refItem = pThis->pItem[i];

			int x = m_nPosX + refItem.nPosX;
			int y = m_nPosY + refItem.nPosY;

			m_pCtrls[i].pObject->SetPos(x, y);
			m_pCtrls[i].pObject->OnDraw(pDC);
		}
	}
}


GTabHead::GTabHead()
{
	m_nTab = 0;
	m_nActive = 0;
}

GTabHead::~GTabHead()
{
}

SINT GTabHead::GetTab()const
{
	return m_nTab;
}

SINT GTabHead::SetTab(SINT nTab)
{
	SINT nOld = m_nTab;
	m_nTab = nTab;
	return nOld;
}

SINT GTabHead::GetActive()const
{
	return m_nActive;
}

SINT GTabHead::SetActive(SINT nAct)
{
	if(nAct >= 0 && nAct < m_nTab)
	{
		m_nActive = nAct;
	}
	return m_nActive;
}

void GTabHead::OnDraw(GDC* pDC)
{
	const CONTROL* pThis = (const CONTROL*)m_pThis;

	if(m_pCtrls && m_nTab > 0 && pThis->nItem >= 3)
	{
		const int iRef = 0;
		const int iBox = 1;
		const int iAct = 2;

		CXY refCxy, actCxy;
		if( m_pCtrls[iRef].pObject->GetCxy(refCxy) &&
			m_pCtrls[iAct].pObject->GetCxy(actCxy) )
		{
			const CITEM& refItem = pThis->pItem[iRef];
			const CITEM& actItem = pThis->pItem[iAct];
			const CITEM& boxItem = pThis->pItem[iBox];
			
			int x = m_nPosX + refItem.nPosX;
			int y = m_nPosY + refItem.nPosY;

			int ax = x;
			int ay = y;

			int i = 0;
			for(; i<m_nActive; i++)
			{
				m_pCtrls[iRef].pObject->SetPos(x, y);
				m_pCtrls[iRef].pObject->OnDraw(pDC);
				x += refCxy.cx;
			}
			if(i == m_nActive)
			{
				ax = x;
				ay = y;
				x += actCxy.cx;
				i++;
			}
			for(; i<m_nTab; i++)
			{
				m_pCtrls[iRef].pObject->SetPos(x, y);
				m_pCtrls[iRef].pObject->OnDraw(pDC);
				x += refCxy.cx;
			}

			int bx = m_nPosX + boxItem.nPosX;
			int by = m_nPosY + boxItem.nPosY;

			m_pCtrls[iBox].pObject->SetPos(bx, by);
			m_pCtrls[iBox].pObject->OnDraw(pDC);

			m_pCtrls[iAct].pObject->SetPos(ax, ay);
			m_pCtrls[iAct].pObject->OnDraw(pDC);
		}

	}
}

SINT GTabHead::HitTest(int x, int y)const
{
	const CONTROL* pThis = (const CONTROL*)m_pThis;

	if(m_pCtrls && m_nTab > 0)
	{
		const int iRef = 0;
		const int iAct = 2;

		CXY refCxy, actCxy;
		if( m_pCtrls[iRef].pObject->GetCxy(refCxy) &&
			m_pCtrls[iAct].pObject->GetCxy(actCxy) )
		{
			const CITEM& refItem = pThis->pItem[iRef];
			
			BOX box;
			box.left = m_nPosX + refItem.nPosX;
			box.top = m_nPosY + refItem.nPosY;
			box.right = box.left + refCxy.cx;
			box.bottom = box.top + refCxy.cy;

			int i = 0;
			for(; i<m_nActive; i++)
			{
				if(PtInBox(x, y, box))
				{
					return i;
				}
				box.left += refCxy.cx;
				box.right += refCxy.cx;
			}
			if(i == m_nActive)
			{
				box.left += actCxy.cx;
				box.right += actCxy.cx;
				i++;
			}
			for(; i<m_nTab; i++)
			{
				if(PtInBox(x, y, box))
				{
					return i;
				}
				box.left += refCxy.cx;
				box.right += refCxy.cx;
			}
		}
	}
	return -1;
}

PMRV GTabHead::ProcMsg(GMsg& refMsg)
{
	const CONTROL* pThis = (const CONTROL*)m_pThis;

	if(pThis)
	{
		switch(refMsg.nMsg)
		{
		case MSG_LBUTTONDOWN:
			{
				SINT nSel = HitTest(refMsg.x, refMsg.y);
				if(nSel != -1)
				{
					SetActive(nSel);
					refMsg.nNotify = NTF_SELCHANGED;
					return PM_NEXT;
				}
			}
			break;

		case MSG_LBUTTONUP:
			break;
		case MSG_MOUSEMOVE:
			break;
		}
	}
	return PM_PASS;
}


GTabView::GTabView()
{
}

GTabView::~GTabView()
{
}

BOOL GTabView::DataBind(GSight* pSight, const BYTE* pBase, SceneDat::PTR ptr)
{
	if(GControl::DataBind(pSight, pBase, ptr))
	{
		if(m_pCtrls)
		{
			const CONTROL* pThis = (const CONTROL*)m_pThis;

			SINT nTab = pThis->nItem - 1;
			if(nTab > 0)
			{
				const int iHead = 0;
				GTabHead* pTabHead = m_pCtrls[iHead].pTabHead;
				pTabHead->SetTab(nTab);
				pTabHead->SetActive(0);

				for(int i=2; i<pThis->nItem; i++)
				{
					m_pCtrls[i].pControl->SetVisible(false);
				}
				return TRUE;
			}
		}
	}
	return FALSE;
}

SINT GTabView::GetActive()const
{
	if(m_pCtrls)
	{
		const int iHead = 0;
		GTabHead* pTabHead = m_pCtrls[iHead].pTabHead;
		return pTabHead->GetActive();
	}
	return -1;
}

SINT GTabView::SetActive(SINT nAct)
{
	if(m_pCtrls)
	{
		const int iHead = 0;
		GTabHead* pTabHead = m_pCtrls[iHead].pTabHead;
		SINT nOld = pTabHead->SetActive(nAct);
		nAct = pTabHead->GetActive();

		m_pCtrls[nOld+1].pControl->SetVisible(false);
		m_pCtrls[nAct+1].pControl->SetVisible(true);

		return nOld;
	}
	return -1;
}


PMRV GTabView::ProcMsg(GMsg& refMsg)
{
	if(m_pCtrls)
	{
		const int iHead = 0;
		GTabHead* pTabHead = m_pCtrls[iHead].pTabHead;

		SINT nAct = pTabHead->GetActive();

		PMRV nRet = pTabHead->ProcMsg(refMsg);
		if(nRet != PM_PASS)
		{
			if(refMsg.nNotify == NTF_SELCHANGED)
			{
				SINT nNew = pTabHead->GetActive();

				m_pCtrls[nAct+1].pControl->SetVisible(false);
				m_pCtrls[nNew+1].pControl->SetVisible(true);
			}
		}
		else
		{
			nRet = m_pCtrls[nAct+1].pControl->ProcMsg(refMsg);
		}

		return nRet;
	}

	return PM_PASS;
}

//////////////////////////////////////////////////////////////////////////

GEditor::GEditor()
{
	memset(&m_edit, 0, sizeof(FOCUS_EDIT));

	m_edit.nWidth = 64;
	m_edit.nHeight = 20;

	m_nFocus = 0;

	m_bNew = FALSE;
	m_pText = NULL;
	m_pFont = NULL;
}

GEditor::~GEditor()
{
	delete[] m_edit.pBuffer;
	m_edit.pBuffer = NULL;
	m_edit.nBuffer = 0;
	m_edit.nText = 0;
	if(m_pText)
	{
		GDC::ReleaseText(m_pText);
		m_pText = NULL;
	}
	m_pFont = NULL;
}

LPCTSTR GEditor::GetText(SINT* pnText)const
{
	if(pnText != NULL)
	{
		*pnText = m_edit.nText;
	}
	if(m_edit.nText > 0)
	{
		return m_edit.pBuffer;
	}
	return NULL;
}

SINT GEditor::SetType(SINT nType, SINT nMaxLength)
{
	SINT nOldType = m_edit.nType;
	m_edit.nType = nType;
	m_edit.nMaxLength = nMaxLength;
	return nOldType;
}

GFont* GEditor::SetFont(GFont* pFont)
{
	GFont* pOld = m_pFont;
	m_pFont = pFont;
	return pOld;
}

COLOR GEditor::SetColor(COLOR color)
{
	COLOR old = m_edit.nColor;
	m_edit.nColor = color;
	return old;
}

BOOL GEditor::SetText(LPCTSTR pText, SINT nText)
{
	if(pText && nText)
	{
		m_bNew = TRUE;

		if(nText+1 > m_edit.nBuffer)
		{
			m_edit.nBuffer = nText+1;
			if(m_edit.nType == ETYPE_DEFAULT)
			{
				if(m_edit.nMaxLength > 0)
				{
					if(m_edit.nBuffer < m_edit.nMaxLength)
					{
						m_edit.nBuffer = m_edit.nMaxLength + 1;
					}
				}
				else
				{
					if(m_edit.nBuffer < 16)
					{
						m_edit.nBuffer = 16;
					}
				}
			}
			delete[] m_edit.pBuffer;
			m_edit.pBuffer = new TCHAR[m_edit.nBuffer];
		}
		m_edit.nText = 0;
		if(m_edit.pBuffer)
		{
			_tcsncpy(m_edit.pBuffer, pText, nText);
			m_edit.pBuffer[nText] = 0;
			m_edit.nText = nText;
			return TRUE;
		}
	}
	else
	{
		m_edit.nText = 0;
		return TRUE;
	}
	return FALSE;
}


BOOL GEditor::GetCxy(CXY& refCxy)const
{
	refCxy.cx = m_edit.nWidth;
	refCxy.cy = m_edit.nHeight;
	return TRUE;
}

BOOL GEditor::SetCxy(SINT cx, SINT cy)
{
	m_edit.nWidth = cx;
	m_edit.nHeight = cy;
	return TRUE;
}

PMRV GEditor::ProcMsg(GMsg& refMsg)
{
	if(refMsg.nMsg == MSG_EDITOR_TEXT)
	{
		FOCUS_EDIT* pFocus = (FOCUS_EDIT*)(refMsg.dwParam2);

		if(pFocus == &m_edit)
		{
			m_bNew = (BOOL)(refMsg.dwParam1);
			return PM_HOLD;
		}
	}
	else if(refMsg.nMsg == MSG_LBUTTONDOWN)
	{
		if(HitTest(refMsg.x, refMsg.y) != -1)
		{
			m_edit.x = m_nPosX;
			m_edit.y = m_nPosY;

			m_nFocus = 1;
			refMsg.nCtrlId = m_nCtrlId;
			refMsg.nNotify = NTF_EDIT_FOCUS;
			refMsg.dwParam2 = (DWORD)&m_edit;
			if(m_edit.pBuffer == NULL)
			{
				m_edit.nBuffer = m_edit.nMaxLength > 0 ?
					m_edit.nMaxLength + 2 : 16;
				m_edit.pBuffer = new TCHAR[m_edit.nBuffer];
			}
			return PM_HOLD;
		}
		else if(m_nFocus > 0)
		{
			m_nFocus = 0;
			if(refMsg.nNotify != NTF_EDIT_FOCUS)
			{
				refMsg.nCtrlId = m_nCtrlId;
				refMsg.nNotify = NTF_KILL_FOCUS;
			}
			return PM_NEXT;
		}
	}
	else if(refMsg.nMsg == MSG_KEYCHAR)
	{
		if(m_nFocus > 0)
		{
			TCHAR c = (TCHAR)refMsg.dwParam1;
			if( (c >= '!' && c <= '}') )
			{
				InsertTail(c);
				m_nFocus = 1;
			}
			else if(c == VK_BACK)
			{
				DeleteTail(1);
			}
		}
	}
	return PM_PASS;
}

void GEditor::InsertTail(TCHAR c)
{
	if(m_edit.pBuffer == NULL)
	{
		m_edit.nBuffer = m_edit.nMaxLength > 0 ? m_edit.nMaxLength : 16;
		m_edit.pBuffer = new TCHAR[m_edit.nBuffer];
		m_edit.nText = 0;
	}
	if(m_edit.pBuffer != NULL)
	{
		if(m_edit.nMaxLength > 0 && m_edit.nText >= m_edit.nMaxLength)
		{
			return;
		}

		if(m_edit.nText+2 >= m_edit.nBuffer)
		{
			SINT nBuffer = m_edit.nText + 16;
			LPTSTR pBuffer = new TCHAR[nBuffer];
			if(pBuffer)
			{
				memcpy(pBuffer, m_edit.pBuffer, sizeof(TCHAR)*m_edit.nBuffer);
				delete[] m_edit.pBuffer;
				m_edit.pBuffer = pBuffer;
				m_edit.nBuffer = nBuffer;
			}
		}
		if(m_edit.pBuffer && (m_edit.nText+1 < m_edit.nBuffer))
		{
			m_edit.pBuffer[m_edit.nText] = c;
			m_edit.nText++;
			m_edit.pBuffer[m_edit.nText] = 0;
		}
	}
}

void GEditor::DeleteTail(SINT nTail)
{
	if(nTail < 0) // delete all
	{
		m_edit.nText = 0;
	}
	else if(m_edit.pBuffer && m_edit.nText > 0)
	{
		m_edit.nText -= nTail;
		m_edit.nText = m_edit.nText > 0 ? m_edit.nText : 0;
		m_edit.pBuffer[m_edit.nText] = 0;
	}
}

void GEditor::Update(UINT nMillisecond)
{
	GControl::Update(nMillisecond);
	if(m_nFocus > 0)
	{
		m_nFocus++;
	}
}

void GEditor::OnDraw(GDC* pDC)
{
	//GControl::OnDraw(pDC);
	GSprite* pSprite = NULL;
	GSprite* pCaret = NULL;
	const CONTROL* pThis = (const CONTROL*)m_pThis;

	if(pThis && m_pCtrls)
	{
		for(int i=0; i<pThis->nItem; i++)
		{
			const CITEM& refItem = pThis->pItem[i];
			if(refItem.nType == SceneDat::tSprite)
			{
				if(pSprite == NULL)
					pSprite = m_pCtrls[i].pSprite;
				else
					pCaret = m_pCtrls[i].pSprite;
			}
		}
	}

	int x = m_nPosX;
	int y = m_nPosY;

	if(pSprite && m_edit.pBuffer && m_edit.nText > 0)
	{
		LPCTSTR p = m_edit.pBuffer;
		LPCTSTR pEnd = p + m_edit.nText;

		pSprite->SetAction(0);
		if(m_edit.nType == ETYPE_DEFAULT)
		{
			if(m_edit.nText && m_edit.pBuffer && m_nFocus == 0)
			{
				if(m_bNew)
				{
					m_bNew = FALSE;
					pDC->ReleaseText(m_pText);
					m_pText = pDC->PrepareText(m_edit.pBuffer, 
						m_edit.nText, m_pFont, m_edit.nColor);
				}
				if(m_pText)
				{
					pDC->DrawText(x, y, *m_pText);
				}
			}
		}
		else if(m_edit.nType != ETYPE_PASSWORD)
		{
			while(p < pEnd)
			{
				SINT i = -1;
				if(*p >= _T('!') && *p <= _T('}'))
				{
					i = *p - _T('!');
				}
				else
				{
					x += 12;
				}
				if(i != -1)
				{
					CXY cxy = pSprite->DrawClip(pDC, x, y, i);
					x += cxy.cx;
				}
				if(x - m_nPosX > m_edit.nWidth)
					break;

				p++;
			}
		}
		else // draw * for password
		{
			LPCTSTR pTail = pEnd - 1;

			if(m_nFocus > 5 || m_nFocus == 0)
			{
				pTail = pEnd;
			}

			while(p < pTail)
			{
				SINT i = _T('*') - _T('!');
				if(i != -1)
				{
					CXY cxy = pSprite->DrawClip(pDC, x, y, i);
					x += cxy.cx;
				}
				if(x - m_nPosX > m_edit.nWidth)
					break;

				p++;
			}
			while(p < pEnd)
			{
				SINT i = -1;
				if(*p >= _T('!') && *p <= _T('}'))
				{
					i = *p - _T('!');
				}
				else
				{
					x += 12;
				}
				if(i != -1)
				{
					CXY cxy = pSprite->DrawClip(pDC, x, y, i);
					x += cxy.cx;
				}
				if(x - m_nPosX > m_edit.nWidth)
					break;

				p++;
			}
		}
	}

	if(m_nFocus > 0 && pCaret != NULL)
	{
		if(m_edit.nType == ETYPE_DEFAULT)
		{
			pCaret->SetVisible(false);
		}
		else
		{ 
			pCaret->SetPos(x, y);
			pCaret->SetVisible(true);
			pCaret->OnDraw(pDC);
		}
	}

}
